2011. március 9., szerda

Konstruktorok

Két kis feladat egy nagy multinacionális cég tesztjéből:

1) Mi a hiba az alábbi programban? Miért?

class X {
    X(int i) {
        System.out.println(i);
    }
}

class Y extends X {
    Y(int i) {
        System.out.println(i);
    }
}

2) Mi a hiba az alábbi kódban? Miért?

class Base {
    int i;
    Base(int j) {
        i = j;
    }
}

class Derived extends Base {
    Derived(int j) {
        System.out.println(j);
        super(j);
    }
}

A megoldás:

1)

A kód nem fordul, mert nincs az X osztályban alapértelmezett konstruktor. Feloldható az Y osztályban a
super(i);
utasítás használatával a
System.out.println(i);
helyett.

2)

Itt a helyzet hasonló, tetézve azzal, hogy a szülő konstruktorát a super kulcsszóval mindig a legelső helyen kell meghívni.


A miértre is egyszerű a válasz, aki elolvassa az alábbiakat, meg fogja érteni. (A sietősek kedvéért kiemeltem a lényeget.)


Ha egy osztály nem ad meg semmilyen konstruktort, akkor a fordító automatikusan biztosít egyet: ez a paraméterek nélküli alapértelmezett konstruktor (default constructor).

  • Ha az osztály az Object leszármazottja, egy üres (paraméterek nélküli) alapértelmezett konstruktort kap
  • Egyéb esetben pedig a szülő alapértelmezett (paraméterek nélküli) konstruktorát hívja meg a super kulcsszó használatával

Fordítási idejű hibát fogunk látni, ha utóbbi esetben az ősosztály alapértelmezett konstruktora nem elérhető vagy nincs.

Alapértelmezett konstruktor nem dobhat semmilyen kivételt (a nem alapértelmezettek dobhatnak, ezt a throws kulcsszó használatával kell jelezni). Az alapértelmezett konstruktor olyan elérhetőséggel rendelkezik, amilyennel az osztályt magát deklaráljuk.

Azaz, minden osztálynak van konstruktora, akkor is, ha explicit azt nem adtuk meg. Azonban, figyeljünk arra, hogy az alapértelmezett konstruktort csak akkor kapja meg automatikusan egy osztály, ha semmilyen konstruktora nincs. Ha már csak egyet is megadunk, akkor ez a szabály nem él. Ez okozhat problémákat, többek között a fenti esetekben is. Az ökölszabály tehát:

Ha bármilyen konstruktort definiálsz egy osztályban, akkor az alapértelmezettet mindenképpen meg kell adni.



Azaz a válasz a miértre az, hogy mivel az Y osztályunk nem hívja meg az ősosztályának egyetlen konstruktorát sem, a fordító azt feltételezi, hogy az ősosztály alapértelmezett konstruktorát hívjuk az első helyen, de az X osztályban, mivel explicit megadtunk már egy konstruktort, a fordító nem rakja be az alapértelmezettet, azaz nem tudja azt meghívni, így hibát jelez. Ez a helyzet a második esetben is, a már tárgyalt super kulcsszó rossz elhelyezése mellett.


Olvasnivaló:
Java language specification 3rd edition
Java world - Understanding constructors
Java Tutorial: Constructors

2011. március 6., vasárnap

Equals és a hashCode

public boolean equals(Object obj)

  • ellenőrzi, hogy egy másik objektum egyenlő-e azzal az objektummal, amire ezt a metódust meghívtuk
  • az alapértelmezett implementáció azt ellenőrzi, hogy két objektum referenciája azonos-e (x==y), ezt hívjuk sekély hasonlításnak (shallow comparison)
  • azon osztályok, melyek felüldefiniálják ezt a metódust, mély hasonlítást (deep comparison) kell használniuk, azaz minden egyes releváns adatát össze kell hasonlítani az objektumnak
Az equals tulajdonságai:
  • reflexív
  • szimmetrikus
  • tranzitív
  • konzisztens (ha két objektum egyenlő, akkor amíg nem módosulnak, egyenlőnek kell maradniuk)
  • null összehasonlítás biztos (ha null az objektum, amivel összehasonlítjuk, mindig hamisat kell visszaadni)
  • ha két objektum egyenlő, hashCode-juk is azonos kell legyen

public int hashCode()

  •  az adott objektum hash kódjának egész számú reprezentációját adja vissza
  • a hash alapú collecition-ök (HashTable, HashMap stb.) használják
  • minden osztály, ami az equals() metódust felüldefiniálja, a hashCode() metódust is felül kell, hogy definiálja

A hashCode() tulajdonságai:
  • konzisztens 
  • ha két objektum egyenlő az equals() metódus szerint, hash kódjuk is azonos kell legyen
  • ugyanazzal a hash kóddal rendelkező objektumok lehetnek különbözőek
Ugyan nem megkövetelt, de célszerű úgy programozni, hogy különböző objektumoknak különböző legyen a hash kódja is, ezzel javítunk az általunk implementált típusú objektumokat tartalmazó, hash alapján működő collection-ök teljesítményén.

Összefoglalás

Ami nagyon fontos, és el szokott siklani a figyelme sokaknak e felett:

Ha két objektum azonos (egyenlő), ugyanazt a hash kódot kell adniuk is, de fordítva ez nem igaz, azaz ha két objektum azonos hash kóddal rendelkezik, attól még nem biztos, hogy egyenlőek is.



Irodalom:
Java 1.5 API

Izolációs szintek adatbázisokban

Adatbázisokkal kapcsolatban fontos megemlíteni, hogy a tranzakciók megbízhatóságát négy tulajdonág befolyásolja. Ezeket ACID tulajdonságoknak nevezik szaknyelven (ACID = Atomicity, Consistency, Isolation, Durability, azaz magyarul atomicitás, konzisztencia, izoláció és tartósság).

Aki adatbázisokkal foglalkozik, annak illik ismernie az izolációs szinteket. Az izolációs szint egy olyan tulajdonság, mely azt határozza meg, hogy az egyidejűleg futó adatbázis tranzakciók láthatják-e egymás változtatásait, illetve ha igen, akkor hogyan. A gyakorlatban minél magasabb az izolációs szint, annál nagyobb mértékben használ az adatbázis-kezelő zárolást (lock), azaz annál nagyobb a zárolásból eredő többletterhelés, valamint a holtpont kialakulásának (deadlock) a lehetősége is, viszont minél alacsonyabb az izolációs szint, annál inkább fordulhatnak elő bizonyos nemkívánatos jelenségek adatbázisból olvasáskor.

A négy izolációs szint (fentről lefelé csökkenő sorrendben):
  1. Serializable
  2. Repeatable reads
  3. Read uncommitted
  4. Read committed

Serializable

Ez a legmagasabb izolációs szint. Minden tranzakció teljesen izoláltan fut le, mintha egymás után hajtódnának végre. Az adatbázis-kezelő végrehajthat több tranzakciót is egyszerre, de csakis akkor, ha a soros feldolgozás látszata fenntartható, azaz ugyanazt az eredményt kapjuk, mintha a két vagy több tranzakció egymás után hajtódott volna végre.

  • az olvasási és írási zárolás a tranzakció végén szűnik meg
  • range lock (SELECT .. WHERE lekérdezések esetén) a phantom read elkerülésére (ld. lejjebb)

Repeatable reads

  • az írási és olvasási zárolás a tranzakció végén szűnik meg
  • nincs range lock → phantom read anomália előfordulhat

Read committed

  • az írási zárolás a tranzakció végén szűnik meg
  • az olvasási zárolás csak a SELECT művelet végéig tart → non-repeatable reads anomália előfordulhat (ld. lejjebb)
  • nincs range lock → phantom read anomália előfordulhat

Read uncommitted


Ez a legalacsonyabb izolációs szint.

  • dirty reads anomália is előfordulhat (ld. lejjebb)


Olvasási anomáliák


Phantom read

Ha egy tranzakció lefutásakor 2 azonos lekérdezés hajtódik végre egymás után és más eredménnyel térnek vissza, akkor azt phantom read-nek nevezzük. Ez akkor fordulhat elő, ha SELECT .. WHERE tartománylekérdezések esetén nem használunk tartomány zárolást (range lock), azaz T1 tranzakció lekérdez egy tartományt, majd a műveletet még egyszer végrehajtja egy tranzakción belül, de a kettő között T2 tranzakció olyan sorokat inzertál a T1 általá használ táblába,melyek kielégítik a WHERE feltételt, azaz beleesnek a tartományba.


Non-repeatable reads

Ez akkor fordulhat elő, ha egy T1 tranzakció végrehajt egy lekérdezést, de mielőtt az befejeződne (a COMMIT hívással), T2 tranzakció felülírja a T1 által olvasott sor(oka)t. A T1 tranzakció tehát a régi értékekről tud, de már újak vannak a táblában.
  • serializable / repeatable reads izolációs szinteken a régi értékeket kell visszakapni a T1 tranzakcióból
  • read committed / read uncommitted izolációs szinteken az adatbázis-kezelő visszaadhatja az új értéket is akár
Dirty reads

Ha egy T1 tranzakció olvashatja egy másik, T2 tranzakció által módosított adatokat, mielőtt a T2 befejeződne a COMMIT hívással, azt dirty read-nek nevezzük. Azaz, a tranzakciók még nem kommitált (azaz visszahívható) adatokat is olvashatnak az adatbázisból.

Összefoglalás

Izolációs szintek és anomáliák

Izolációs szint Dirty read Non-repeatable read Phantom read
Read uncommitted X X X
Non-repeatable read - X X
Repeatable read - - X
Serializable - - -

Izolációs szintek és a zárolási típusok

Izolációs szint Range lock Read lock Write lock
Read uncommitted - - -
Non-repeatable read - - X
Repeatable read - X X
Serializable X X X


Irodalom:
http://en.wikipedia.org/wiki/ACID
http://en.wikipedia.orgwiki/Isolation_level