You are on page 1of 11

Java GYIK (Gyakori Interjú Kérdések)

Ez a blog azért jött létre, mert tapasztalatom szerint még egy nem kezdő Java
programozót is érhetnek meglepetések manapság állásinterjún. Többen azt
javasolták, gyűjtsem össze azokat a témaköröket, kérdéseket, jegyzeteket, amiket
hosszú estéken át olvasgattam, hogy egy helyen legyen. Íme.

2011. március 21., hétfő

Változók alapértelmezett értékei


Minden változónak rendelkeznie kell értékekkel, mielőtt használnánk őket. A Java nyelvben minden
változó kap alapértelmezett értéket, erre a szabályok a következőek:

Minden osztályváltozó, példányváltozó vagy tömb elem az alapértelmezett  értékével inicializálódik:


A byte típus alapértelmezett értéke nulla, azaz (byte)0
A short típus alapértelmezett értéke nulla, azaz (short)0
Az int típus alapértelmezett értéke nulla, azaz 0
A long típus alapértelmezett értéke nulla, azaz 0L
A float típus alapértelmezett értéke (pozitív) nulla, azaz 0.0f
A double típus alapértelmezett értéke (pozitív) nulla, azaz 0.0d
A char típus alapértelmezett értéke a null karakter, azaz '\u0000'
A boolean típus alapértelmezett értéke hamis, azaz false
Referencia típusok alapértelmezett értéke null

Minden metódus paraméter a hívó által használt megfelelő argumentum értékével inicializálódik.

Minden konstruktor paraméter a példányosító kifejezés vagy explicit konstruktorhívás megfelelő


argumentumának értékével inicializálódik.

Egy kivételkezelő paraméter a dobott kivételt reprezentáló objektummal inicializálódik.

Minden lokális változónak explicit értéket kell adni, mielőtt használják, vagy inicializációval vagy
hozzárendeléssel (assignment).

Bejegyezte: Nagy Gábor dátum: 3/21/2011  0 megjegyzés 

2011. március 18., péntek

Kódértelmezési példa
Egy pici elgondolkodtató feladat egy interjúról:

Tyre.java:

public class Tyre {

private int size;

Tyre(int s) {
size = s;
}

public int getSize() {


return size;
}

public void setSize(int size) {


this.size = size;
}

Car.java:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Car {

private List<Tyre> tires = new ArrayList<Tyre>();

public void setTires(List<Tyre> tires) {


this.tires.clear();
this.tires.addAll(tires);
}

public void addTire(Tyre tire) {


this.tires.add(tire);
}

public List<Tyre> getTires() {


return Collections.unmodifiableList(tires);
}

CarTest.java:

import java.util.List;

public class CarTest {

/**
* Question: what will be the output?
*/
public static void main(String[] args) {
Car car = new Car();
car.addTire(new Tyre(10));
car.addTire(new Tyre(11));

List<Tyre> carTires = car.getTires();


System.out.println("Before: " + carTires.size());
car.setTires(carTires);
System.out.println("After (1): " + car.getTires().size());
System.out.println("After (2): " + carTires.size());
}

Kérdés: Mi lesz a kimenete a programnak?

Bejegyezte: Nagy Gábor dátum: 3/18/2011  7 megjegyzés 

Címkék: példa

Osztályok típusai
A legtöbbet olyan osztályokat használunk, amelyek nem másik osztályba vagy interfészbe vannak
beágyazva. Az ilyen osztályt legfelső szintű osztálynak nevezzük (top level class).

A Java nyelv megengedi osztályok deklarálását más osztályokban is, ezeket beágyazott osztályoknak
nevezzük (nested class). Ezeknek több fajtája van.

Egy beágyazott osztály kétféle lehet: statikus vagy nem statikus. Az első fajtát egyszerűen csak statikus
beágyazott osztálynak nevezzük (static nested class), a második neve pedig belső osztály (inner class). A
beágyazott osztály tagja az őt tartalmazó osztálynak, azaz a nem statikus beágyazott osztályok
közvetlenül elérik a tartalmazó osztály más tagjait is, a statikusak azonban nem. Míg egy normális, azaz
felső szintű osztály csak public vagy package private lehet, addig a beágyazott osztályok mind a négy
féle hozzáféréssel rendelkezhetnek: public, protected, private és package private.

Beágyazott osztályok használata

logikailag összetartozó osztályok (ha egy osztályt csak egyetlen másik használ, a kettőt egy
helyen lehet deklarálni és egyiket a másikba ágyazni)
növeli az enkapszulációt (példa: van két osztályunk, az egyiknek el kell érnie a másik
adattagjait, melyek egyébként privátként lennének deklarálva - ebben az esetben ha
beágyazzuk az osztályt, az adattagok elérhetőek maradnak a beágyazott osztály számára,
viszont nem lesznek láthatóak más osztályok számára)
könnyebben fenntartható (olvasható) kód

Statikus beágyazott osztályok

Mint normális esetben, a statikus beágyazott osztály nem érheti el a nem statikus tagokat (csak ha
példányreferencián keresztül teszi azt) és osztályreferencián keresztül hivatkozunk rá.

TopLevelClass.StaticNestedClass instance = new TopLevelClass.StaticNestedClass();


Belső osztályok

Egy belső osztály egy példánya a beágyazó osztály példányával együtt - azon belül - él, azaz maga nem
hivatkozhat semmilyen statikus tagra, de bármilyen példányváltozóra igen.

TopLevelClass top = new TopLevelClass();


TopLevelClass.InnerClass myObject = top.new InnerClass();

Két speciális belső osztályról kell még beszélni, ezek a név nélküli (anonymous classes) és lokális
osztályok (local classes).

Lokális osztályok

Deklarálhatunk egy belső osztályt egy metóduson belül is. Ezek a lokális (belső) osztályok.

a lokális osztály csak azon a blokkon belül használható, ahol definiálták


lokális osztály deklarálásakor nem használhatóak
a public, protected, private, statickulcsszavak (ezek csak az osztály tagjainál használhatóak,
lokális változó vagy osztály deklarálásakor nem)
a fentiek miatt statikus tagot / metódust / osztályt sem tartalmazhatnak a lokális osztályok,
csak konstansokat, ha az static final - ként van deklarálva
a lokális osztály eléri a lokális változókat, metódus paramétereket, kivétel paramétereket is,
de akkor és csak akkor, ha azok final - ként vannak deklarálva (Erre az a magyarázat, hogy
mivel a lokális osztályok élettartama hosszabb lehet, mint magának a metódusnak a lefutási
ideje, a fordító a lokális változókról egy másolatot hoz létre, amit a lokális osztály használni
fog. A másolat és a lokális változó közötti azonosságot csak úgy garantálhatjuk, hogy a lokális
változó final - ként van deklarálva.)

Név nélküli osztályok

Az előzőt megtehetjük úgy is, hogy nem adunk nevet az osztályunknak, az ilyeneket nevezzük anoním /
név nélküli belső osztályoknak, ilyenkor az osztály példányosítása és definiálása egyszerre, egy helyen
történik.

mivel az anoním osztálynak nincs neve, konstruktora sem lehet; ha szükség van konstruktorra,
akkor vagy lokális osztályt kell használni, vagy inicializáló blokkot, amit pont erre a helyzetre
találtak ki
hasonlóan a lokális osztályokhoz, nem használhatóak
a public, protected, private, statickulcsszavak és statikus mezőket stb. sem lehet definiálni
benne, kivéve ha az final is
akkor ajánlott használni, ha az osztályt, amit megadunk, rögtön használjuk is vagy csak
egyszer kell használni, esetleg az osztálynak nagyon rövid a megvalósítása.

Példa név nélküli osztály használatára

Vegyük pl. a java.io.File osztály list() metódusát, ami kilistázza a könyvtárban lévő fájlokat. Mielőtt
azonban visszaadja a listát, egy szűrőn futtatja át azokat, amit nekünk kell megadni paraméterként.
Amikor egy ilyen fájl szűrőt adunk meg, akkor gyakorlatilag egy adapter osztályt definiálunk, amely
olyan kódot definiál, amit egy másik objektum hív meg. Adapter osztályokat általában célszerű anoním
osztályként megadni.
File myFile = new File("/src");
String[] listOfFiles = myFile.list(new FilenameFilter() {
public boolean accept(File f, String s) { return s.endsWith(".java"); }
});

Tagosztályok (member classes)

A tagosztály olyan nem statikus osztály,mely közvetlenül egy másik osztályban vagy interfészben van. A
tagosztály neve nem lehet ugyanaz, mint a befoglaló osztály neve és nem tartalmazhat statikus változót
/ metódust / osztályt sem, kivéve, ha az static final.

Bejegyezte: Nagy Gábor dátum: 3/18/2011  0 megjegyzés 

Címkék: osztály

2011. március 17., csütörtök

Absztrakt osztályok és interfészek


Egy osztályt az abstract kulcsszóval absztrakt osztályként lehet deklarálni, ami annyit jelent,
hogy tartalmazhat absztrakt metódusokat. Absztrakt osztályt nem lehet példányosítani, csak más
osztályt származtatni belőle, aminek meg kell adnia az esetleges absztrakt metódusok implementációját
(ha nem adja meg az implementációkat, akkor a származtatott osztályt is absztraktként kell deklarálni).
Egy metódust szintén az abstract kulcsszó használatával lehet absztrakt metódusként deklarálni és nem
adhat meg implementációt, azaz csak deklarálni kell.

Absztrakt osztály tartalmazhat static mezőket és/vagy metódusokat is, ezeket ugyanúgy
osztályreferencián keresztül lehet elérni, mint normális esetben. 

Absztrakt osztály és interfész közötti különbségek

az absztrakt osztály hasonló az interfészhez, de megadhat részleges implementációt, amit a


származtatott osztályok egészíthetnek ki
ha ansztrakt osztályunk csak absztrakt metódusokat tartalmaz, akkor azt inkább interfészként
kell deklarálni
az interfészek biztosítják a Java nyelvben a többszörös öröklődést; egy osztály több interfészt
is implementálhat egyszerre, függetlenül attól, hogy azok mennyire állnak kapcsolatban
egymással (pl. Serializable, Comparable, Cloneable stb.)
az absztrakt osztály leszármazott osztályai közös implementációt is tartalmaznak (az
absztrakt osztályban nem absztraktként definiált metódusok), de egyedi viselkedést is
megadnak (ezek az absztrakt metódusok)
absztrakt osztály tartalmazhat nem static és final mezőket is
interfészekben minden metódus implicit absztraktként van deklarálva
Absztrakt osztály mint interfész implementáció

Ha egy osztály implementál egy interfészt, akkor annak minden egyes metódusát implementálnia kell. Ez
alól egyetlen kivétel az absztrakt osztály. A fordító akkor sem fog panaszkodni, ha az interfész metódus
meg sem jelenik, mint absztrakt metódus az absztrakt osztályban. Ezeket majd a származtatott
osztályokban kell csak implementálni.

Bejegyezte: Nagy Gábor dátum: 3/17/2011  1 megjegyzés 

Címkék: interfész, osztály

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

Bejegyezte: Nagy Gábor dátum: 3/09/2011  0 megjegyzés 


Címkék: példa

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

Bejegyezte: Nagy Gábor dátum: 3/06/2011  0 megjegyzés 

Címkék: equals, hashcode

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

You might also like