Spletni dnevnik
ccpcreations.com v2
LANGUAGE


SEKCIJE
Vse
Android Development
J2ME aplikacije
Kdo sem
Moje skladbe
Moji programi
Nasveti za Javo
Projekti
Triki in namigi
Varnostna politika - Privacy Policy
Zanimivosti


ISKANJE


BLOGI
bohak.si
zelenik.net


POVEZAVE
Mozilla Firefox
Android


ČLANKI PRED LETOM DNI
Datum: 09. oktober 2010
Video Live Wallpaper 0.83b for Android


OCENE OGLEDANIH FILMOV
Avatar 5
Star Trek 4.5
(500) Days of Summer 4.5
Moon 4
2012 3


ZADNJI ODGOVORI
From: Matic
Re: Kako do izbrisanih dat
From: Filip
Re: Video Live Wallpaper 0
From: okokkji

From: hideo
Re: WiimoteController 0.3
From: Katarina



ZA SMETIŠČARJE!
behind.ballgaq@hotmail.com
light-shefds@yahoo.com
settle.cutamg@netscape.com
face_stopudt@yahoo.com
will_playrbo@hotmail.com


Blog zamrznjen -- Blog Frozen

Ta spletni dnevnik je zamrznjen. To pomeni, da ne bo več prejemal novih prispevkov in komentarjev. Kmalu bo tudi arhiviran in s tem prestavljen na trajnejše mesto. Temu sporočilu sledi običajna vsebina spletnega dnevnika.

Hvala za razumevanje.
This blog has been frozen. This means it will no longer accept new posts and comments. It will soon be archived and moved to a more permanent location. Below this message you can find the blog's usual contents.

Thank you for your understanding.
Naloga 6: I/O tokovi, izjeme, množice objektov

Standardni zahtevnik: prosim, da, v kolikor želite to besedilo objaviti kje drugje, to storite tako, da linkate povezavo do te strani, ne pa da kopirate besedilo na drugo stran. Za kakršne koli napake ne odgovarjam (vse je možno, saj je to besedilo pisano na roko). Če opazite kakšno napako, mi sporočite. Rade volje bom popravil, priznal svojo zmoto in se vam zahvalil ; )


NAJ TI NAVETI NE BODO (EDINA?) OSNOVA ZA OPRAVLJANJE IZPITA! ZA MOREBITNI NEOPRAVLJEN IZPIT NE ODGOVARJAM!

Izjeme v Javi

Java obravnava izjemna stanja z t.i. izjemami. Izjeme so (tako kot večina ostalih stvari v Javi) seveda objekti. Vse izjeme so izpeljane iz razreda Exception, ki torej označuje najbolj splošen tip izjeme. Exception je izpeljan iz razreda Throwable, ki ima še enega otroka, imenovanega Error. To so napake, ki pa nas v tem poglavju ne zanimajo.

V glavnem ločimo izjeme na dva tipa. Na tašne, ki se zgodijo ob času izvajanja in ki jih ne moremo napovedati. Takim izjemam pravimo nenapovedane izjeme (Run-time exception). So pa tudi takšne izjeme, ki jih razredne metode v Javi prožijo namenoma z namenom, da bi svojega klicatelja ali Java stroj obvestili o izjemnem stanju med svojim delovanjem. Tem pa pravimo napovedane izjeme. Velja, da so vse izjeme napovedane, razen izjeme RuntimeException in vseh njenih izpeljank.

Napovedane izjeme smo pri programiranju dolžni obravnavati. To lahko storimo na dva načina. Prvi je, da klic metode, ki napoveduje možnost metanja izjeme, ovijemo v try/catch blok. Primer:
Konstruktor razreda java.io.FileReader(String datoteka), napoveduje možnost metanja izjeme, imenovane FileNotFoundException. To bi se zgodilo takrat, ko ob uporabi tega konstukrorja datoteka ne bi obstajala. Naš program more vrstico s klicem tega konstruktorja (torej tam, kjer piše new FileReader("datoteka" )) obravnavati kot potencialni vir izjeme. Napišemo to:
try {
datoteka=new FileReader("datoteka.txt" ) );
}
catch(FileNotFoundException e) {
System.out.println("Datoteka ne obstaja" );
}

Izjemo FileNotFoundException obravnavamo v tem primeru tako, da jo "ujamemo" v primeru, če jo sporna vrstica "vrže". Če torej me odpiranjem datoteka ne bo obstajala, bo program napisal sporočilo Datoteka na obstaja. Če bo datoteka obstaja, jo bo program normalno odprl in catch blok se ne bo izvedel (sporočilo se ne bo izpisalo). Obstoječima blokoma try/catch lahko dodamo še tretjega:
finally {
System.out.println("Odpiranje končano" );
}

Ta blok se bo vedno izvedel, neglede na to, če datoteka obstaja ali ne (torej če se je izjema zgodila ali ne).

Drug način je, da metodo, v kateri kličemo sporno vrstico, deklariramo z dodatnim napotkom: namesto na primer
public void mojaMetoda() {
....
new FileReader()
....
}

lahko napišemo:
public void mojaMetoda() throws FileNotFoundException {
....
new FileReader()
....
}


V takem primeru nam ni treba uporabljati try/catch blokov nikjer, kjer bi se lahko prožila izjema FileNotFoundException. Če se izjema zgodi, jo naša metoda "vrže" klicatelju. Z "throws" direktivo smo torej naredili našo metodo takšno, da lahko vrže izjemo FileNotFoundException. Naša metoda se sedaj glede napovedi metanja izjem obnaša enako kot konstruktor FileReader(datoteka). Torej more tisti, ki jo kliče, obravnavati izjemo, ki jo je prej morala naša metoda. To lahko spet stori bodisi z try/catch blokom ali spet na ta način (zaradi česar bo mogel klicatelj te metode obravnavati to izjemo).


Izjeme lahko tudi namerno prožimo, če želimo našega klicatelja obvestiti o izjemnem stanju. V ta namen ustvarimo objekt izjeme in ga vržemo z ukazom throw. Primer:
Exception izjema=new Exception();
throw izjema;

Zadnja vrstica je izvor izjem in jo moremo obravnavati kot takšno. Torej moremo v primeru, da meče napovedano izjemo, le-to obravnavati. Lahko bi jo sicer ovili v try/catch blok, vendar to nebi imelo smisla, saj bi tako lovili izjemo, ki smo jo namenoma prožili z namenom, da obvestimo našega klicatelja o izjemnem dogodku. Bolj smiselna je deklaracija naše metode z direktivo throws Exception, da bo to izjemo prejel naš klicatelj in jo bo moral obravnavati. Če vržemo nenapovedan tip izjeme (npr. throw new RuntimeException()), nam ni potrebno pisati niti try/catch bloka, niti direktive throws. Če se bo taka izjema zgodila, se bo med delovanjem izpisala v konzoli (gotovo ste že videli podoben izpis izjeme NullPointerException, ki je tudi tipa Run-time).

Velja še opozoriti, da lahko namesto na primer FileNotFoundException lovimo tudi IOException ali celo Exception. FileNotFoundException je namreč podrazred IOException, ta pa je podrazred Exception. Vendar bo ob takem lovljenju vlovljena vsakršna izjema tipa IOException oziroma Exception, ne le FileNotFoundException. Če damo bloku try{} več blokov catch(...){} (to namreč lahko storimo, da lovimo več različnih tipkov napak), moremo loviti najprej najbolj specialne napake. Torej mora biti catch(FileNotFoundException E) {} pred catch(Exception e) {}. V nasprotnem primeru bo blok z izjemo Exception vlovil tudi FileNotFoundException, in posledično se blok, ki lovi FileNotFoundException (in ki je za blokom za Exception) ne bo nikoli izvedel.





Tokovi

Java za vhodne/izhodne operacije uporablja t.i. tokove. Tokovi so uporabni za veliko stvari, med vsemi so najpomembnejše:


Tokovi so v k njižnici java.io. Večina temeljnih tokov je izpeljanih iz štirih abstraktnih razredov:

Pri teh razredih (in vseh nadaljnih izpeljankah) je treba paziti na izjeme, ki jih prožijo njihove metode. Vse izjeme so tipa IOException (seveda obstajajo bolj specifične izjeme, več informacij dobite v Java API dokumentaciji). Te izjeme je treba ob uporabi loviti z try/catch bloki ali z "metanjem dalje".

Razredi, ki se končajo z Stream, delajo z bajti (zlogi; ponavadi 8-bitne binarne vrednosti), medtem ko razredi, ki se končajo z Reader/Writer, delajo z znaki (ponavadi 16-bitne vrednosti, ki predstavljajo znake v človeški pisavi). Temeljni razred je vsak tak razred, ki ustvari tok, ki se poveže z vhodno/izhodnim objektom (bodisi datoteko, bodisi I/O napravo ipd). Temeljni tok je tok, ki ga ustvari temeljni razred (izraz temeljni ni uradni izraz za to funkcionalnost!). Najpomembnejši temeljni razredi za delo z datotekami so in imajo naslednje funkcionalnosti:

FileInputStream
new FileInputStream(File datoteka)
new FileInputStream(String imeDatoteke)

Ta razred ustvari tok, ki bo črpal binarne podatke iz datoteke, odprte za branje. Vsak ukaz, ki bo zahteval podatke iz tega toka, jih bo dobil iz odprte datoteke.

FileOutputStream
new FileOutputStream(File datoteka)
new FileOutputStream(File datoteka, boolean dodati)
new FileOutputStream(String imeDatoteke)
new FileOutputStream(String imeDatoteke, boolean dodati)

Ta razred ustvari tok, ki se bo stekal v binarno datoteko, odprto za pisanje. Vsak ukaz, ki bo poslal podatke v ta tok, jih bo zapisal v odprto datoteko. Datoteka se pred odprtjem ustvari (oziroma pobriše, če je že obstajala). Če želimo dodajati podatke obstoječi datoteki, ne da bi jo počistili, nastavimo dodati/I] na [I]true.

FileReader
podobni konstruktorji kot pri FileInputStream
Ta razred ustvari tok, ki bo črpal znakovne podatke iz datoteke.

FileWriter
podobni konstruktorji kot pri FileOutputStream
Ta razred ustvari tok, ki se bo stekal v tekstovno datoteko.

PrintWriter
Glejte naslednje poglavje. PrintWriter je opisan tam.





Dekoracija

Dekoracija je proces, pri katerem "ovijemo" temeljni tok z enim ali več dekorativnimi tokovi. Ti tokovi dajejo toku dodatno funkcionalnost. Temeljni tokovi za delo z datotekami omogočajo na primer le delo z posameznimi zlogi oziroma znaki. Z dekoriranjem pa lahko s ti tokovi delamo na primer s števili, zapisi, tabelami, vrsticami znakov, objekti itd. Če želimo na primer osnovnemu toku iz binarne datoteke dodati možnost pretvarjanja bitov v števila, ga ovijemo na naslednji način:
FileInputStream fis=new FileInputStream("Datoteka.dat" ); //to je osnovni vhodni tok iz datoteke
DataInputStream dis=new DataInputStream(fis); //fis smo ovili z dekoracijo DataInputStream
dis.readInt() //Ta metoda vzame 32 bitov iz osnovnega toka in jih pretvori v številko tipa int



Najpogosteje uporabljeni dekorirni razredi so:

BufferedInputStream
new BufferedInputStream(InputStream ovitiTok)
new BufferedInputStream(InputStream ovitiTok, int velikost)

Ta razred dekorira ovitiTok s funkcionalnostjo medpomnjenja. Medpomnjenje (buffering) je postopek, pri katerem se iz vhodnega toka berejo večji kosi podatkov vnaprej z namenom, da se proces branja pohitri. Drugačno branje se navzven ne pozna (še vedno uporabljamo iste metode branja, ki nam vračajo isto količino podatkov), pozna se le v hitrosti izvajanja branja (delo se prej konča). Velikost kosa, ki se naenkrat prebere, lahko nastavimo z argumentom velikost. Večji kosi pomenijo manj operacij branja, a večjo rabo pomnilnika.

DataInputStream
new DataInputStream(InputStream ovitiTok)
Ta razred dekorira binarni ovitiTok s funkcionalnostjo branja osnovnih podatkovnih tipov. Razred opremi ovitiTok z množico razničnih read metod, ki berejo iz ovitega toka različne količine bitov in jih pretvarjajo v različne osnovne Javine podatkovne tipe. readInt() prebere 32 bitov in jih vrne kot int, readByte() prebere 8 bitov in jih vrne kot zlog, readFloat prebere 32 bitov in jih vrne kot float. Teh metod je še več, za informacije glejte Java API (klik na podnaslov DataInputStream).

ObjectInputStream
new ObjectInputStream(InputStream ovitiTok)
Ta razred dekorira binarni ovitiTok s funkcionalnosto branja serializiranih objektov. Več o serializiranih objektih pozneje.

InputStreamReader
new InputStreamReader(InputStream ovitiTok)
new InputStreamReader(InputStream ovitiTok, Charset tabelaZnakov)
new InputStreamReader(InputStream ovitiTok, String imeTabeleZnakov)

Ta razred je most med binarnimi in znakovnimi tokovi. Ta razred pretvarja binarni tok v znakovni tok po pravilih tabele znakov. Če mu je ne določimo, uporablja trenutno (najverjetneje lokalno) tabelo znakov. Nekatere tabele:


BufferedReader
new BufferedReader(Reader ovitiTok)
new BufferedReader(Reader ovitiTok, int velikost)

Ta razred dekorira tekstovni tok z funkcionalnostjo medpomnjenja. Ta razred doda med drugim tudi metodo .readLine(), ki iz ovitega toka prebere eno vrstico besedila.



BufferedOutputStream
new BufferedOutputStream(OutputStream ovitiTok)
new BufferedInputStream(InputStream ovitiTok, int velikost)

Ta razred dekorira izhodni ovitiTok s funkcionalnostjo medpomnjenja. (podobno, kot vhodni tok dekorira BufferedInputStream). Medpomnjenje (buffering) je postopek, pri katerem se v izhodni tok ne piše vsak podatek posebej, ampak večji kosi podatkov naenkrat, z namenom, da se proces pisanja pohitri. Drugačno pisanje se navzven ne pozna (še vedno uporabljamo iste metode pisanja, s katerimi pošiljamo v tok isto količino podatkov), pozna se le v hitrosti izvajanja pisanja (delo se prej konča). Velikost kosa, ki se naenkrat zapiše, lahko nastavimo z argumentom velikost. Večji kosi pomenijo manj operacij pisanja, a večjo rabo pomnilnika.

DataOutputStream
new DataOutputStream(OutputStream ovitiTok)
Ta razred dekorira binarni ovitiTok s funkcionalnostjo pisanja osnovnih podatkovnih tipov (podobno kot to za branje tipov naredi DataInputStream). Za informacije glejte Java API.

ObjectOutputStream
new ObjectOutputStream(OutputStream ovitiTok)
Ta razred dekorira binarni ovitiTok s funkcionalnosto pisanja serializiranih objektov. Več o serializiranih objektih pozneje.

OutputStreamWriter
new OutputStreamWriter(OutputStream ovitiTok)
new OutputStreamWriter(OutputStream ovitiTok, Charset tabelaZnakov)
new OutputStreamWriter(OutputStream ovitiTok, String imeTabeleZnakov)

Ta razred je most med znakovnimi in binarnimi tokovi. Ta razred pretvarja znakovni tok po pravilih tabele znakov v binarni tok. Če mu tabele ne določimo, uporablja trenutno (najverjetneje lokalno). Nekatere tabele:


BufferedWriter
new BufferedWriter(Writer ovitiTok)
new BufferedReader(Writer ovitiTok, int velikost)

Ta razred dekorira izhodni tekstovni tok s funkcionalnostjo medpomnjenja (podobno, kot za vhodni tok to stori razred BufferedReader).

PrintWriter
new PrintWriter(File datoteka)
new PrintWriter(String imeDatoteke)
new PrintWriter(OutputStream izhodniBinarniTok)
new PrintWriter(Writer izhodniTekstovniTok)

Ta razred lahko deluje kot temeljni ali kot dekorator izhodnega toka. Posebnost tega razreda je v tem, da dekorira izhodni tok z print in println metodami, s katerimi lahko izpisujemo podatke v tok na enak način, kot jih v konzolo (mimogrede - tudi konzola je tok, imenovan standardni izhod - ob zagonu aplikacije je že odprt, in ga lahko uporabljamo kot OutputStream (ime standardnega izhoda je System.out ) ) .



Najpogostejše dekoracije

Branje binarne datoteke:
bis=new BufferedInputStream(new FileInputStream("datoteka.dat" ) );
int a=bis.read(); //prebere zlog iz datoteke in ga vrne kot int
bis.close();


Pisanje v binarno datoteko:
bos=new BufferedOutputStream(new FileOutputStream("datoteka.dat" ) );
bos.write(int zlog); //zapiše zlog
bos.flush(); //dokonča zapisovanje
bos.close();


Branje tekstovne datoteke:
br=new BufferedReader(new FileReader("datoteka.txt" ) );
int a=br.read(); //prebere znak iz datoteke
String b=br.readLine(); //prebere vrstico besedila iz datoteke
br.close();


Pisanje v tekstovno datoteko:
bw=new BufferedWriter(new FileWriter("datoteka.txt" ) );
bw.write(int znak); //zapiše znak
bw.write(String niza, int začetek, int konec); //zapiše del niza
bw.newLine(); //začne novo vrstico
bw.flush();
bw.close();






Serializacija

Objekte (konkretne primerke razredov) se v javi da pretvoriti v tok podatkov. Ta tok podatkov se nato prenese v datoteko/preko omrežja... Na tak način lahko bodisi shranimo stanje našega programa, bodisi prenesemo dejanske objekte v oddaljen program ipd. Vsak objekt, ki ga želimo serializirati, mora implementirati vmesnik Serializable. Ta vmesnik nima metod, ki bi jih morali implementirati, služi le temu, da sporočimo prevajalniku in Java stroju, da je to objekt, ki se lahko serializira.

Serializacijo uporabljamo tako, da ustvarimo nov tok, ki ga dekoriramo z razredom ObjectOutputStream (oziroma ObjectIntputStream, če gre za vhodni tok). Ta razred ustreznemu toku dodata metode za (de)serializacijo. Če želimo nek objekt serializirati v datoteko, napišemo naslednje (recimo, da je naš objekt tipa String, imenovan zapis):
ObjectOutputStream ous = new ObjectOutputStream(new FileOutputStream("datoteka" ) );
ous.writeObject(zapis);
ous.flush();
ous.close();


Ta koda bo zapisala naš objekt zapis v datoteko. Večina razredov v Javi, ki jih je smiselno serializirati, že implementirajo vmesnik Serializable in jih lahko mirno serializiramo. Med njimi je tudi razred String.

Serializiramo lahko le razrede (torej vse, kar podeduje od java.lang.Object). Osnovnih tipov (int, float, ...) tako ne moremo direktno serializirati. Namesto tega jih "ovijemo" v njihove ustrezne razredne ovojnice, npr.:
ous.writeObject(new Integer(stevilo) );


Objekte serializiramo na naslednji način:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("datoteka" ) );
String mojNiz=(String)ois.readObject();
ois.close();

Bodite pozorni na Castanje v pravilni tip. Branje iz vhodnega toka z metodo .readObject() namreč vrne objekt, a je ta tipa Object. Če ga torej želimo pravilno uporabljati, ga moremo Castati v pravilni tip (to za samo potegne posledico, da ob deserializaciji moramo vedeti, kakšne objekte serializiramo, oziroma da se ob serializaciji izgubi podatek o tipu objekta).





Množice objektov

Množice objektov v Javi so razredi, ki omogočajo dinamično pomnjenje množic objektov. So bolj napredni od preprostih tabel (polj). Ena opazna prednost pred polji je v tem, da so polja dokončne velikosti, medtem ko se razredi za množice objektov dinamično večajo in nimajo posebej dane zgornje meje (zgornja meja obstaja, a je omejena z največjo količino pomnilnika, ki je dodeljen aplikaciji - kar je preko dovolj). Zaradi pomanjkanja časa bom zaenkrat omenil le razred java.util.Vector, ki je tudi najpogosteje uporabljen.

Ustvarjanje novega vektorja:
Vector vektor=new Vector();

V tako ustvarjen vektor lahko dodamo kakšnekoli objekte. Boljša metoda deklaracije je ta:
Vector<String> vektor=new Vector<String>();

V tako ustvarjen vektor lahko vnašamo le zapise (String); torej je preprečena možnost dodajanja objektov napačnega tipa. V takšnem vektorju so objekti tudi "precast-ani" (v splošnem vektorju jih je treba pri branju cast-ati v naš tip).

Dodajanje objekta v vektor:
String zapis="Prvi zapis";
vektor.add(zapis);


Ugotvaljanje velikosti vektorja (število vnešenih objektov v njemu):
int velikost=vektor.size();

Dodajanje objekta na specifično mesto:
vektor.add(1, zapis);

Ta ukaz bo v vektor vrinil zapis na drugo mesto (indeks 0 je prvo mesto, 1 je drugo). Ostale (če ni zadnji) premakne po indeksih v desno. Proži izjemo, če je indeks večji od velikosti vektorja ali <0).

Brisanje objekta iz specifičnega mesta:
vektor.remove(1);

Ta ukaz odstrani drugi objekt iz vektorja. Vse nadaljne objekte premakne po indeksih v levo (tako da zapolni vrzel, ki je pri odstanitvi nastala).
vektor.remove(zapis);

Ta ukaz odstrani prvo instanco objekta zapis, ki je v njem. Vse nadaljne objekte premakne po indeksih v levo (tako da zapolni vrzel, ki je pri odstanitvi nastala).

Čiščenje vektorja
vektor.clear();

Ta ukaz počisti vsebino vektorja.

Prelet skozi objekte v Vektorju:
Enumeration en=vektor.elements();
while(en.hasMoreElements() ) {
System.out.println(en.nextElement() );
}

Ta koda preleti skozi vse objekte v Vektorju in jih izpiše. Jedro kode seveda spremenite po želji.
Nasveti za Javo - 2. junij 2007 @ 14:20 - Ogledov: 6365 - Komentarjev: 0
Komentarji
Ni komentarjev.
Komentirajte ta vnos (registracija ni potrebna)
Tega vnosa žal ni dovoljeno komentirati.