java

SCWCD jegyzetek

A Sun Certified Web Component Developer vizsga hasonló volt, mint az SCJP. Lentebb a múltkorihoz hasonló jegyzet néhány dologról, ami eszembe jutott tanulás közben, kérdezték valamelyik tesztkérdésben, érdemes megjegyezni, vagy épp elrontottam a valamelyik feladatban. Az angol szövegek többnyire javadoc-ból származnak, a gyakorlatiasabb dolgok pedig Tomcat 6 alatt kipróbált tesztek eredményeként.

A lista pontjait az általam használt Charles Lyons-féle SCWCD Study Companion (2nd Edition) c. könyv fejezetei alapján csoportosítottam. A web.xml-es és TLD-s részeknél néhol az XML elemek neveivel egybeírtam az előfordulásaiknak lehetséges számát is (?; 1; +; *).

Az előbbi könyvön kívül érdemes belenézni a Head First Servlets and JSP-be is, mert volt pár apróság, amiről az egyik könyv biztosan állította, hogy nem lesz vizsgán, miközben azt a másik úgy tárgyalta, mint rendes vizsgaanyag - és fordítva is. A Head First utolsó száz oldala ráadásul egy teljes tesztvizsga. Ehhez viszont ajánlott a webes hibajegyzéket is átvezetni a könyvbe, mert sajnos elég sok elírás van benne.

UTF-8-e vagy?

Egy érdekes probléma: hogyan döntsük el egy fájlról, hogy milyen karakterkódolással készült? Igazából algoritmus kellene rá, a Total Commander F3-as nézőkéje nem mindig jó megoldás. Főleg a bemenet gépi ellenőrzésekor nem.

Kis keresgélés után a Mozilla-féle karakterkódolás-detektáló algoritmus java verziójára akadtam. Kipróbáltam, de nem jött be. A latin2 és ASCII kódolást felismerte, az UTF-8-at és a latin1-et nem. Pedig az UTF-8 lenne a legérdekesebb. Igazából egy boolean isUtf8(byte[]) metódus is elegendő lehet a legtöbb feladatra.

Aztán találtam belőle egy fejlettebb változatot, ami már az UTF-8-cal is megbirkózik.

Egy másik jó választás a GuessEncoding. Ez ugyan csak az UTF-8 kódolást ismeri (meg néhány másikat a BOM-ból képes kitalálni), de cserébe viszonylag egyszerű, könnyen módosítható.

Kipróbáltam a Java API idevágó osztályait is, hátha jelzik a hibát, ha UTF-8 helyett valami mást találnak. Fájlok beolvasásával kapcsolatban három módszert nézegettem. Az első a FileReader volt, de a dokumentációja szerint az alapértelmezett karakterkódolással dolgozik, ha nem erre van szükségünk, akkor az InputStreamReader és a FileInputStream kombinációját kell alkalmaznunk. A harmadik jelölt pedig a Scanner osztály volt.

Mivel a FileReader-nek nincs olyan konstruktora, amelynek meg lehetne adni a karakterkódolást, így ezzel nem is foglalkoztam tovább. A rendszer alapértelmezett karakterkódolása biztos, hogy rendszerről-rendszerre változhat, úgyhogy erre nem érdemes építeni. Az InputStreamReader és Scanner osztályok rendelkeznek olyan konstruktorral, ami karakterkészletet vár. Ezeknek mindig UTF-8-at adtam meg.

A Scanner nem jelzi, ha hibát talál az UTF-8 fájlban. Egyszerűen csak onnantól kezdve nem ad vissza semmit. Az InputStreamReader visszaad valamit, de nem az igazi. A Scanner reakciója szimpatikusabb, legalább utalni fog valami a hibára, ami lehet, hogy egyébként észrevehetetlen lenne. Például ha egy nagyobb adatfájl belsejében fordul elő egy hibás kódolású karakter.

Az eddigiek miatt tanácsos lehet a tényleges adatfeldolgozás megkezdése előtt az egész fájl UTF-8 megfelelőségét ellenőrizni. Erre a GuessEncoding váza tökéletes, csupán néhány módosítás szükséges hozzá:

  • Ha nem sikerül detektálni a karakterkódolást, akkor ne a rendszer alapértelmezett karakterkódolását adja vissza, hanem dobjon mondjuk egy UnknownCharsetException-t.
  • Az egész fájlt be kell olvasnunk, nem elég csak a fájl első néhány kilobájtja alapján tippelni. (Ahogyan azt egyébként a Mozilla-féle algoritmus is teszi.)

A magyar ékezetes karakterekhez egyébként egy reguláris kifejezés is elegendő lehet. (Csak ne Scannerrel olvassuk be a fájlt, mert akkor csak az első hibás karakter előtti tartalmat kapjuk meg, amire valószínűleg illeszkedni fog a kifejezés.)

SCJP csapdák

Néhány dolog ami eszembe jutott a Sun Certified Java Programmer (SCJP) vizsgára való tanulás közben, kérdezték valamelyik tesztkérdésben, érdemes megjegyezni, vagy épp elrontottam a valamelyik feladatban.

SCJP lettem

Ma túlestem a régóta halogatott SCJP vizsgán. Számalk, Etele út. Korrektek voltak. A cég megszokásból 310-055-re vett vouchert, amit szerencsére módosítottak 310-065-re, ami már Java 6 SCJP, nem Java 5. Könyvem is már a Java 6-hoz van. Kimaradt a System.gc(), volt helyette NavigableSet és NavigableMap. Legalábbis ez a két változás tűnt fel.

A vizsga egész könnyűnek tűnt a könyvben, CD mellékleten, vagy a netről pluszban letölthető feladatsorokhoz képest. A hibákat nem mutatják meg sajna, bár talán jobb is, mert biztos csak felhúztam volna magam rajta.

A netről letölthető feladatsoron már sikerült kiakadnom a hétvégén. Nem igazán élvezem a többoldalas kódban a fordítási hibák keresgélését. Keresse meg a javac, azért van. A „fejből javadoc-ot” feladatokat sem szeretem, de élesben kevés ilyen volt, azok is egyszerűek.

A netes tesztnél az tette be a kaput, amikor a végén közölte, hogy „failed”. A CD-sek előtte simán megvoltak. Aztán jobban megnéztem, itt 80 százalék a minimum, nem 65. Para ezerrel: akkor most mennyi is kell? A netes frissebb lenne? A 80 azért kicsit izgulós. Könyv elő, abban 65 százalékot írnak, meg azt, hogy ez változhat, pontos infó a suned.sun.com-on. Ott 65-öt írnak. Huhh...

A 65 százalék pedig ennél a próbatesztnél is simán megvolt, ráadásul pár hosszabb forráskódot egyszerűen kihagytam, mert nem volt már kedvem végigolvasni sem őket. (Aztán persze fordítási hibás volt egyik-másik...)

A legjobban az a megoldókulcsbeli hiba tetszett (volt pár...), amikor a „Compilation fails due to a single error in the code” és ugyanez „multiple errors”-szal is igaz volt egyszerre. Legalábbis szerintük.

A vizsgán emberi méretű forráskódok voltak, nem egy 800x600 pixeles ablakban kellett görgetni (egérgörgő nélkül) a forrást, mint a CD-ről felrakható MasterExam-ban. Teljes képernyő, rendes betűtípus a forráskódok alatt, stb. Volt drag&drop is. A feladatok negyede talán nem (mint ahogy a könyv írja), de mondjuk 10-12 kérdés volt ilyen a 72-ből. Egyszer próbáltam újra megnézni egy ilyen kérdést, de szólt a progi, hogy ha megteszem, akkor elvesznek a korábbi válaszok (ha jól értelmeztem), úgyhogy inkább hagytam az eredeti verziót.

Eredményt csak a nyomtató mutat a végén, a képernyőre már nem írják ki. Gondolom így izgalmasabb.

private

Kapunk fordítási hibát a kommentelt soroknál a Java fordítótól?

class A {
	private int a;
	
	class InnerA {
		private int b;
		
		void m2() {
			a = 9; // #1
		}
	}

	void m1() {
		InnerA innerA = new InnerA();
		innerA.b = 8; // #2
	}

}

Válasz hajtás után.

Code review után

Eclipse 99+ megnyitott fájllal.

throw null

Nemrég összefutottam a következőhöz erősen hasonlító kódrészlettel:

} catch (final Exception e) {
	if (e != null)
		e.printStackTrace();
}

Tipikus „mitírki”-t lehet belőle készíteni, mondjuk egy ilyet:

try {
	throw null;
} catch (final Exception e) {
	if (e == null) {
		System.out.println("null");
	} else {
		System.out.println("nemnull");
	}
	System.out.println("e: " + e);
}

Eredmény hajtás után.

Másfél nap

import javax.jcr.Node;

final Node child = ...;
if (child != null) {
	child.remove();
	log.debug("Removing child [path = " + child.getPath() + "]");
}

Mi a hiba a fenti kódban? Válasz hajtás után.

InputStream JAXB-n át

A múltkor JAXB-vel kellett átvinnem egy InputStream-et is tartalmazó osztályt. Neten nem találtam rá semmi értelmeset, így lásd lentebb az én megoldásom.

Az InputStream szerializálásához egy adapterosztályt írtam, ami bájttömbbé alakítja az InputStream-et, illetve ugyanezt megteszi a másik irányba is. A bájttömböt pedig már tudja a JAXB is kezelni. Ezután már csak a szerializálandó osztály InputStream mezőjét kell a következőképp annotálni:

@XmlJavaTypeAdapter(InputStreamAdapter.class)
private InputStream data;

Az adapterosztály pedig a következő:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.bind.annotation.adapters.XmlAdapter;

/**
 * JAXB Adapterosztály InputStream-ek szerializálásához.
 * 
 * @author palacsint
 */
public class InputStreamAdapter extends XmlAdapter<byte[], InputStream> {

    @Override
    public byte[] marshal(final InputStream inputStream) throws Exception {
        return inputStreamToByteArray(inputStream);
    }

    @Override
    public InputStream unmarshal(byte[] byteArray) throws Exception {
        return new ByteArrayInputStream(byteArray);
    }

    public static byte[] inputStreamToByteArray(final InputStream inputStream) throws IOException {
        final ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
        try {
            final byte[] buffer = new byte[1024];

            int len;
            while ((len = inputStream.read(buffer)) >= 0) {
                out.write(buffer, 0, len);
            }
        } finally {
            inputStream.close();
            out.close();
        }
        return out.toByteArray();
    }
}

Persze ilyenkor a kiolvasás miatt elvész az adat az InputStream-ből.

Exporting non-public type through public API

Adott a következő Java fájl:

package package2;

public class Callee {
    public InnerClass m1() {
        return new InnerClass();
    }

    public void m2(final InnerClass innerClass) {
    }
}

class InnerClass {
    public void m3() {
    }
}

Vegyük észre, hogy az InnerClass default elérési módosítóval rendelkezik, így csak a package2 csomagon belül látható.

És egy másik csomagban lévő kódrészlet, ami az előbbieket használja:

final Callee callee = new Callee();
callee.m1();
callee.m1().m3();
callee.m2(null);

A fenti három metódushívásból van-e olyan, amelyik fordítási hibát okozni? Ha igen, akkor mely, illetve melyek azok?

Válasz hajtás után.

Tartalom átvétel